﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

// Открывает форму ввода параметров администрирования информационной базы и/или кластера.
//
// Параметры:
//  ОписаниеОповещенияОЗакрытии - ОписаниеОповещения - обработчик, который будет вызван после ввода параметров
//	                                                   администрирования.
//  ЗапрашиватьПараметрыАдминистрированияИБ - Булево - признак необходимости ввода параметров администрирования
//	                                                   информационной базы.
//  ЗапрашиватьПараметрыАдминистрированияКластера - Булево - признак необходимости ввода параметров администрирования
//	                                                         кластера.
//  ПараметрыАдминистрирования - см. СтандартныеПодсистемыСервер.ПараметрыАдминистрирования.
//  Заголовок - Строка - заголовок формы, описывающий для чего запрашиваются параметры администрирования.
//  ПоясняющаяНадпись - Строка - пояснение для выполняемого действия, в контексте которого запрашиваются параметры.
//
Процедура ПоказатьПараметрыАдминистрирования(ОписаниеОповещенияОЗакрытии, ЗапрашиватьПараметрыАдминистрированияИБ,
	ЗапрашиватьПараметрыАдминистрированияКластера, ПараметрыАдминистрирования = Неопределено,
	Заголовок = "", ПоясняющаяНадпись = "") Экспорт
	
	ПараметрыФормы = Новый Структура;
	ПараметрыФормы.Вставить("ЗапрашиватьПараметрыАдминистрированияИБ", ЗапрашиватьПараметрыАдминистрированияИБ);
	ПараметрыФормы.Вставить("ЗапрашиватьПараметрыАдминистрированияКластера", ЗапрашиватьПараметрыАдминистрированияКластера);
	ПараметрыФормы.Вставить("ПараметрыАдминистрирования", ПараметрыАдминистрирования);
	ПараметрыФормы.Вставить("Заголовок", Заголовок);
	ПараметрыФормы.Вставить("ПоясняющаяНадпись", ПоясняющаяНадпись);
	
	ОткрытьФорму("ОбщаяФорма.ПараметрыАдминистрированияПрограммы", ПараметрыФормы,,,,,ОписаниеОповещенияОЗакрытии);
	
КонецПроцедуры

// Устанавливает и отключает режим завершения работы пользователей в программе.
// При завершении работы, до наступления момента блокировки, всем активным пользователям
// будет выводиться уведомление о планируемом завершении работы программы и рекомендацией
// сохранить все свои данные.
// Текущий сеанс завершается последним.
//
// Параметры:
//  ЗавершитьРаботу - Булево.
//
Процедура УстановитьРежимЗавершенияРаботыПользователей(Знач ЗавершитьРаботу) Экспорт
	
	УстановитьПризнакРаботаПользователейЗавершается(ЗавершитьРаботу);
	УстановитьКонтрольЗавершенияРаботыПользователей(Не ЗавершитьРаботу);
	
	Если ЗавершитьРаботу Тогда
		ТекущийРежим = СоединенияИБВызовСервера.ПараметрыБлокировкиСеансов(Истина);
		ЗавершитьРаботуПользователей(ТекущийРежим);
	КонецЕсли;
	
КонецПроцедуры

// Позволяет отметить необходимость завершения работы сеанса, включившего блокировку работы
// пользователей в программе.
//
// Параметры:
//   Значение - Булево - Истина, если если текущий сеанс завершать не требуется.
//
Процедура УстановитьПризнакЗавершитьВсеСеансыКромеТекущего(Значение) Экспорт
	
	ПараметрыКлиента().ЗавершитьВсеСеансыКромеТекущего = Значение;
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

// Устанавливает значение переменной РаботаПользователейЗавершается в значение Значение.
//
// Параметры:
//   Значение - Булево - устанавливаемое значение.
//
Процедура УстановитьПризнакРаботаПользователейЗавершается(Значение) Экспорт
	
	ПараметрыКлиента().РаботаПользователейЗавершается = Значение;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистем конфигурации.

// Вызывается при интерактивном начале работы пользователя с областью данных.
//
// Параметры:
//  ПараметрыЗапуска - Массив - массив строк разделенных символом ";" в параметре запуска,
//                     переданным в конфигурацию с помощью ключа командной строки /C.
//  Отказ            - Булево - возвращаемое значение. Если установить Истина,
//                     обработка события ПриНачалеРаботыСистемы будет прервана.
//
Процедура ПриОбработкеПараметровЗапуска(ПараметрыЗапуска, Отказ) Экспорт
	
	Если Не ПодсистемаИспользуется() Тогда
		Возврат;
	КонецЕсли;
	
	Отказ = Отказ Или ОбработатьПараметрыЗапуска(ПараметрыЗапуска);
	
КонецПроцедуры

// См. ОбщегоНазначенияКлиентПереопределяемый.ПередНачаломРаботыСистемы.
Процедура ПередНачаломРаботыСистемы(Параметры) Экспорт
	
	ПараметрыКлиента = СтандартныеПодсистемыКлиент.ПараметрыРаботыКлиентаПриЗапуске();
	
	Если Не ПараметрыКлиента.Свойство("СеансыОбластиДанныхЗаблокированы") Тогда
		Возврат;
	КонецЕсли;
	
	Параметры.ИнтерактивнаяОбработка = Новый ОписаниеОповещения(
		"ИнтерактивнаяОбработкаПередНачаломРаботыСистемы", ЭтотОбъект);
	
КонецПроцедуры

// См. ОбщегоНазначенияКлиентПереопределяемый.ПослеНачалаРаботыСистемы.
Процедура ПослеНачалаРаботыСистемы() Экспорт
	
	Если Не ПодсистемаИспользуется()
	 Или Не ОбщегоНазначенияКлиент.ДоступноИспользованиеРазделенныхДанных()
	 Или ПолучитьСкоростьКлиентскогоСоединения() <> СкоростьКлиентскогоСоединения.Обычная Тогда
		Возврат;
	КонецЕсли;
	
	РежимБлокировки = СтандартныеПодсистемыКлиент.ПараметрыРаботыКлиентаПриЗапуске().ПараметрыБлокировкиСеансов;
	ТекущееВремя = РежимБлокировки.ТекущаяДатаСеанса;
	Если РежимБлокировки.Установлена 
		 И (НЕ ЗначениеЗаполнено(РежимБлокировки.Начало) ИЛИ ТекущееВремя >= РежимБлокировки.Начало) 
		 И (НЕ ЗначениеЗаполнено(РежимБлокировки.Конец) ИЛИ ТекущееВремя <= РежимБлокировки.Конец) Тогда
		// Если пользователь зашел в базу, в которой установлена режим блокировки, значит использовался ключ /UC.
		// Завершать работу такого пользователя не следует.
		Возврат;
	КонецЕсли;
	
	Если СтрНайти(ВРег(ПараметрЗапуска), ВРег("ЗавершитьРаботуПользователей")) > 0 Тогда
		Возврат;
	КонецЕсли;
	
	УстановитьКонтрольЗавершенияРаботыПользователей(Истина);
	
	Если РежимБлокировки.Установлена Тогда
		ПараметрыКлиента().ДатаПоследнегоСообщенияОБлокировке = ОбщегоНазначенияКлиент.ДатаСеанса();
		КонтрольРежимаЗавершенияРаботыПользователей(РежимБлокировки);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Отказ - см. ОбщегоНазначенияКлиентПереопределяемый.ПередЗавершениемРаботыСистемы.Отказ
//  Предупреждения - см. ОбщегоНазначенияКлиентПереопределяемый.ПередЗавершениемРаботыСистемы.Предупреждения
//
Процедура ПередЗавершениемРаботыСистемы(Отказ, Предупреждения) Экспорт
	
	Если РаботаПользователейЗавершается() Тогда
		ПараметрыПредупреждения = СтандартныеПодсистемыКлиент.ПредупреждениеПриЗавершенииРаботы();
		ПараметрыПредупреждения.ТекстГиперссылки = НСтр("ru = 'Блокировка работы пользователей'");
		ПараметрыПредупреждения.ТекстПредупреждения = НСтр("ru = 'Из текущего сеанса выполняется завершение работы пользователей'");
		ПараметрыПредупреждения.ВывестиОдноПредупреждение = Истина;
		
		Форма = "Обработка.БлокировкаРаботыПользователей.Форма.БлокировкаСоединенийСИнформационнойБазой";
		
		ДействиеПриНажатииГиперссылки = ПараметрыПредупреждения.ДействиеПриНажатииГиперссылки;
		ДействиеПриНажатииГиперссылки.Форма = Форма;
		ДействиеПриНажатииГиперссылки.ПрикладнаяФормаПредупреждения = Форма;
		
		Предупреждения.Добавить(ПараметрыПредупреждения);
	КонецЕсли;
	
КонецПроцедуры

// Вызывается при неудачной попытке установить монопольный режим в файловой базе.
//
// Параметры:
//  Оповещение - ОписаниеОповещения - описывает, куда надо передать управление после закрытия формы.
//
Процедура ПриОткрытииФормыОшибкиУстановкиМонопольногоРежима(Оповещение = Неопределено, ПараметрыФормы = Неопределено) Экспорт
	
	ОткрытьФорму("Обработка.БлокировкаРаботыПользователей.Форма.ОшибкаУстановкиМонопольногоРежима", ПараметрыФормы,
		, , , , Оповещение);
	
КонецПроцедуры

// Открывает форму блокировки работы пользователей.
//
Процедура ПриОткрытииФормыБлокировкиРаботыПользователей(Оповещение = Неопределено, ПараметрыФормы = Неопределено) Экспорт
	
	ОткрытьФорму("Обработка.БлокировкаРаботыПользователей.Форма.БлокировкаСоединенийСИнформационнойБазой", ПараметрыФормы,
		, , , , Оповещение);
	
КонецПроцедуры

// Переопределяет стандартное предупреждение открытием произвольной формы активных пользователей.
//
// Параметры:
//  ИмяФормы - Строка - возвращаемое значение.
//
Процедура ПриОпределенииФормыАктивныхПользователей(ИмяФормы) Экспорт
	
	ИмяФормы = "Обработка.АктивныеПользователи.Форма.АктивныеПользователи";
	
КонецПроцедуры

// См. СтандартныеПодсистемыКлиент.ПриПолученииСерверногоОповещения
Процедура ПриПолученииСерверногоОповещения(ИмяОповещения, Результат) Экспорт
	
	Если Не ПодсистемаИспользуется() Тогда
		Возврат;
	КонецЕсли;
	
	ТекущаяДатаСеанса = '00010101';
	СерверныеОповещенияКлиент.ЗакончилосьВремяОжидания("",,, ТекущаяДатаСеанса);
	
	ПараметрыКлиента = ПараметрыКлиента();
	ПараметрыКлиента.ДатаПоследнейПроверкиБлокировки = ТекущаяДатаСеанса;
	
	Если Результат.Установлена Тогда
		ПараметрыКлиента.ДатаПоследнегоСообщенияОБлокировке = ТекущаяДатаСеанса;
	Иначе
		ПараметрыКлиента.ДатаПоследнегоСообщенияОБлокировке = '00010101';
		ОчиститьОповещениеПользователя();
		Возврат;
	КонецЕсли;
	
	Если РаботаПользователейЗавершается() Тогда
		Если Не ПроцедураЗавершитьРаботуПользователейУжеВыполняется() Тогда
			ТекущийРежим = СоединенияИБВызовСервера.ПараметрыБлокировкиСеансов(Истина);
			ЗавершитьРаботуПользователей(ТекущийРежим);
		КонецЕсли;
		
	ИначеЕсли ВключенКонтрольЗавершенияРаботыПользователей() Тогда
		КонтрольРежимаЗавершенияРаботыПользователей(Результат);
	КонецЕсли;
	
КонецПроцедуры

// Параметры:
//  Параметры - см. ОбщегоНазначенияПереопределяемый.ПередПериодическойОтправкойДанныхКлиентаНаСервер.Параметры
//  ОповещенияПолучены - Булево - оповещения за ожидаемый период успешно получены
//                                (или через систему взаимодействия или общий серверный вызов).
//
Процедура ПередПериодическойОтправкойДанныхКлиентаНаСервер(Параметры, ОповещенияПолучены) Экспорт
	
	Если Не ПодсистемаИспользуется() Тогда
		Возврат;
	КонецЕсли;
	
	ТекущаяДатаСеанса = '00010101';
	СерверныеОповещенияКлиент.ЗакончилосьВремяОжидания("",,, ТекущаяДатаСеанса);
	
	ПараметрыКлиента = ПараметрыКлиента();
	
	Если Не ЗначениеЗаполнено(ПараметрыКлиента.ДатаПоследнейПроверкиБлокировки)
	   И Не ЗначениеЗаполнено(ПараметрыКлиента.ДатаПоследнегоСообщенияОБлокировке) Тогда
		ПараметрыКлиента.ДатаПоследнейПроверкиБлокировки = ТекущаяДатаСеанса;
		Возврат;
	КонецЕсли;
	
	Если РаботаПользователейЗавершается() Тогда
		Интервал = 60;
	ИначеЕсли ВключенКонтрольЗавершенияРаботыПользователей() Тогда
		Если ПараметрыКлиента.ДатаПоследнегоСообщенияОБлокировке + 300 > ТекущаяДатаСеанса Тогда
			Интервал = 60;
		ИначеЕсли ОповещенияПолучены Тогда
			Возврат;
		Иначе
			Интервал = 300;
		КонецЕсли;
	Иначе
		Возврат;
	КонецЕсли;
	
	Если ПараметрыКлиента.ДатаПоследнейПроверкиБлокировки + Интервал > ТекущаяДатаСеанса Тогда
		Возврат;
	КонецЕсли;
	ПараметрыКлиента.ДатаПоследнейПроверкиБлокировки = ТекущаяДатаСеанса;
	
	ИмяПараметра = "СтандартныеПодсистемы.ЗавершениеРаботыПользователей.БлокировкаСеансов";
	Параметры.Вставить(ИмяПараметра, Истина);
	
КонецПроцедуры

// См. ОбщегоНазначенияКлиентПереопределяемый.ПослеПериодическогоПолученияДанныхКлиентаНаСервере
Процедура ПослеПериодическогоПолученияДанныхКлиентаНаСервере(Результаты) Экспорт
	
	ИмяПараметра = "СтандартныеПодсистемы.ЗавершениеРаботыПользователей.БлокировкаСеансов";
	Результат = Результаты.Получить(ИмяПараметра);
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	ПриПолученииСерверногоОповещения(ИмяПараметра, Результат);
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Функция ПодсистемаИспользуется()
	
	// См. также функцию СоединенияИБ.ПодсистемаИспользуется
	Возврат Не ОбщегоНазначенияКлиент.РазделениеВключено();
	
КонецФункции

// Возвращаемое значение:
//  Структура:
//   * ЗавершитьВсеСеансыКромеТекущего - Булево
//   * РаботаПользователейЗавершается - Булево
//   * ВыполнятьКонтрольЗавершенияРаботыПользователей - Булево
//   * ПараметрыАдминистрирования - см. СтандартныеПодсистемыСервер.ПараметрыАдминистрирования
//   * ПроцедураЗавершитьРаботуПользователейУжеВыполняется - Булево
//   * ДатаПоследнейПроверкиБлокировки - Дата
//   * ДатаПоследнегоСообщенияОБлокировке - Дата
//
Функция ПараметрыКлиента()
	
	ИмяПараметра = "СтандартныеПодсистемы.ПараметрыЗавершенияРаботыПользователей";
	ПараметрыКлиента = ПараметрыПриложения[ИмяПараметра];
	Если ПараметрыКлиента <> Неопределено Тогда
		Возврат ПараметрыКлиента;
	КонецЕсли;
	
	ПараметрыКлиента = Новый Структура;
	ПараметрыКлиента.Вставить("ЗавершитьВсеСеансыКромеТекущего", Ложь);
	ПараметрыКлиента.Вставить("РаботаПользователейЗавершается", Ложь);
	ПараметрыКлиента.Вставить("ВыполнятьКонтрольЗавершенияРаботыПользователей", Ложь);
	ПараметрыКлиента.Вставить("ПараметрыАдминистрирования", Неопределено);
	ПараметрыКлиента.Вставить("ПроцедураЗавершитьРаботуПользователейУжеВыполняется", Ложь);
	ПараметрыКлиента.Вставить("ДатаПоследнейПроверкиБлокировки",    '00010101');
	ПараметрыКлиента.Вставить("ДатаПоследнегоСообщенияОБлокировке", '00010101');
	
	ПараметрыПриложения.Вставить(ИмяПараметра, ПараметрыКлиента);
	Возврат ПараметрыКлиента;
	
КонецФункции

Процедура УстановитьКонтрольЗавершенияРаботыПользователей(Значение = Истина)
	
	ПараметрыКлиента().ВыполнятьКонтрольЗавершенияРаботыПользователей = Значение;
	
КонецПроцедуры

Функция ВключенКонтрольЗавершенияРаботыПользователей()
	
	Возврат ПараметрыКлиента().ВыполнятьКонтрольЗавершенияРаботыПользователей;
	
КонецФункции

Процедура УстановитьПроцедураЗавершитьРаботуПользователейУжеВыполняется(Значение = Истина)
	
	ПараметрыКлиента().ПроцедураЗавершитьРаботуПользователейУжеВыполняется = Значение;
	
КонецПроцедуры

Функция ПроцедураЗавершитьРаботуПользователейУжеВыполняется()
	
	Возврат ПараметрыКлиента().ПроцедураЗавершитьРаботуПользователейУжеВыполняется;
	
КонецФункции

// Выполнить завершение текущего сеанса, если установлена блокировка соединений 
// с информационной базой.
//
Процедура КонтрольРежимаЗавершенияРаботыПользователей(ТекущийРежим)

	БлокировкаУстановлена = ТекущийРежим.Установлена;
	
	Если Не БлокировкаУстановлена
	 Или ТекущийРежим.Свойство("Параметр")
	   И ТекущийРежим.Параметр = СерверныеОповещенияКлиент.КлючСеанса() Тогда
		Возврат;
	КонецЕсли;
		
	ВремяНачалаБлокировки = ТекущийРежим.Начало;
	ВремяОкончанияБлокировки = ТекущийРежим.Конец;
	
	// ИнтервалЗакрытьСЗапросом и ИнтервалПрекратить имеют отрицательное значение,
	// поэтому, когда идет сравнение этих параметров с разницей (ВремяНачалаБлокировки - ТекущийМомент),
	// то используется "<=", так как данная разница постоянно уменьшается.
	ИнтервалПредупреждения    = ТекущийРежим.ИнтервалОжиданияЗавершенияРаботыПользователей;
	ИнтервалЗакрытьСЗапросом = ИнтервалПредупреждения / 3;
	ИнтервалПрекратитьВМоделиСервиса = 60; // За минуту до начала блокировки.
	ИнтервалПрекратить        = 0; // В момент установки блокировки.
	ТекущийМомент             = ТекущийРежим.ТекущаяДатаСеанса;
	
	Если ВремяОкончанияБлокировки <> '00010101' И ТекущийМомент > ВремяОкончанияБлокировки Тогда
		Возврат;
	КонецЕсли;
	
	ДатаВремениНачалаБлокировки  = Формат(ВремяНачалаБлокировки, "ДЛФ=DD");
	ВремяВремениНачалаБлокировки = Формат(ВремяНачалаБлокировки, "ДЛФ=T");
	
	ТекстСообщения = СоединенияИБКлиентСервер.ИзвлечьСообщениеБлокировки(ТекущийРежим.Сообщение);
	Шаблон = НСтр("ru = 'Рекомендуется завершить текущую работу и сохранить все свои данные. Работа программы будет завершена %1 в %2. 
		|%3'");
	ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Шаблон, ДатаВремениНачалаБлокировки, ВремяВремениНачалаБлокировки, ТекстСообщения);
	
	РазделениеВключено = ОбщегоНазначенияКлиент.РазделениеВключено();
	Если Не РазделениеВключено
		И (Не ЗначениеЗаполнено(ВремяНачалаБлокировки) Или ВремяНачалаБлокировки - ТекущийМомент < ИнтервалПрекратить) Тогда
		
		СтандартныеПодсистемыКлиент.ПропуститьПредупреждениеПередЗавершениемРаботыСистемы();
		ЗавершитьРаботуСистемы(Ложь, ТекущийРежим.ПерезапуститьПриЗавершении);
		
	ИначеЕсли РазделениеВключено
		И (Не ЗначениеЗаполнено(ВремяНачалаБлокировки) Или ВремяНачалаБлокировки - ТекущийМомент < ИнтервалПрекратитьВМоделиСервиса) Тогда
		
		СтандартныеПодсистемыКлиент.ПропуститьПредупреждениеПередЗавершениемРаботыСистемы();
		ЗавершитьРаботуСистемы(Ложь, Ложь);
		
	ИначеЕсли ВремяНачалаБлокировки - ТекущийМомент <= ИнтервалЗакрытьСЗапросом Тогда
		
		ЗадатьВопросПриЗавершенииРаботы(ТекстСообщения, ВремяНачалаБлокировки);
		
	ИначеЕсли ВремяНачалаБлокировки - ТекущийМомент <= ИнтервалПредупреждения Тогда
		
		ПоказатьПредупреждениеПриЗавершенииРаботы(ТекстСообщения, ВремяНачалаБлокировки);
		
	КонецЕсли;
	
КонецПроцедуры

// Выполнить завершение активных сеансов, если превышено время ожидания, а затем
// завершить текущий сеанс.
//
Процедура ЗавершитьРаботуПользователей(ТекущийРежим)

	ВремяНачалаБлокировки = ТекущийРежим.Начало;
	ТекущийМомент = ТекущийРежим.ТекущаяДатаСеанса;
	КоличествоСеансов = ТекущийРежим.КоличествоСеансов;
	
	ОповещениеНажатия = Новый ОписаниеОповещения("ОбработкаОткрытияФормыБлокировкаРаботыПрограммы", ЭтотОбъект);
	
	Если ТекущийМомент < ВремяНачалаБлокировки Тогда
		ТекстСообщения = НСтр("ru = 'Блокировка работы пользователей запланирована на %1.'");
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстСообщения, ВремяНачалаБлокировки);
		ПоказатьОповещениеПользователя(НСтр("ru = 'Завершение работы пользователей'"), 
			ОповещениеНажатия, ТекстСообщения, БиблиотекаКартинок.Информация32);
		Оповестить("ЗавершениеРаботыПользователей",
			Новый Структура("Статус, КоличествоСеансов", "Обновление", КоличествоСеансов));
		Возврат;
	КонецЕсли;
	
	Если КоличествоСеансов <= 1 Тогда
		// Отключены все пользователи, кроме текущего сеанса.
		// В последнюю очередь предлагаем завершить сеанс, запущенный с параметром "ЗавершитьРаботуПользователей".
		// Такой порядок отключений необходим для обновления конфигурации с помощью пакетного файла.
		УстановитьПризнакРаботаПользователейЗавершается(Ложь);
		Оповестить("ЗавершениеРаботыПользователей",
			Новый Структура("Статус, КоличествоСеансов", "Готово", КоличествоСеансов));
		ЗавершитьРаботуЭтогоСеанса();
		Возврат;
	КонецЕсли; 
	
	БлокировкаУстановлена = ТекущийРежим.Установлена;
	Если Не БлокировкаУстановлена Тогда
		Возврат;
	КонецЕсли;
	
	// Если информационная база файловая, то часть соединений не может быть завершена принудительно.
	Если ОбщегоНазначенияКлиент.ИнформационнаяБазаФайловая() Тогда
		Оповестить("ЗавершениеРаботыПользователей",
			Новый Структура("Статус, КоличествоСеансов", "Обновление", КоличествоСеансов));
		Возврат;
	КонецЕсли;
	
	// После начала блокировки сеансы всех пользователей должны быть отключены
	// если этого не произошло пробуем принудительно прервать соединения.
	
	Попытка
		ПараметрыАдминистрирования = СохраненныеПараметрыАдминистрирования();
		Если ОбщегоНазначенияКлиент.КлиентПодключенЧерезВебСервер() Тогда
			СоединенияИБВызовСервера.УдалитьВсеСеансыКромеТекущего(ПараметрыАдминистрирования);
		Иначе 
			СоединенияИБКлиентСервер.УдалитьВсеСеансыКромеТекущего(ПараметрыАдминистрирования);
		КонецЕсли;
		СохранитьПараметрыАдминистрирования(Неопределено);
	Исключение
		УстановитьПризнакРаботаПользователейЗавершается(Ложь);
			ПоказатьОповещениеПользователя(НСтр("ru = 'Завершение работы пользователей'"),
			ОповещениеНажатия,
			НСтр("ru = 'Завершение сеансов не выполнено. Подробности см. в Журнале регистрации.'"),
			БиблиотекаКартинок.Предупреждение32);
		ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(),
			"Ошибка", ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()),, Истина);
		Оповестить("ЗавершениеРаботыПользователей",
			Новый Структура("Статус,КоличествоСеансов", "Ошибка", КоличествоСеансов));
		Возврат;
	КонецПопытки;
	
	УстановитьПризнакРаботаПользователейЗавершается(Ложь);
	ПоказатьОповещениеПользователя(НСтр("ru = 'Завершение работы пользователей'"),
		ОповещениеНажатия,
		НСтр("ru = 'Завершение сеансов выполнено успешно'"),
		БиблиотекаКартинок.Информация32);
	Оповестить("ЗавершениеРаботыПользователей",
		Новый Структура("Статус,КоличествоСеансов", "Готово", КоличествоСеансов));
	ЗавершитьРаботуЭтогоСеанса();
	
КонецПроцедуры

// Завершает работу (последнего) сеанса администратора, который инициировал завершение работы пользователей.
//
Процедура ЗавершитьРаботуЭтогоСеанса(ВыводитьВопрос = Истина)
	
	УстановитьПризнакРаботаПользователейЗавершается(Ложь);
	
	Если ЗавершитьВсеСеансыКромеТекущего() Тогда
		Возврат;
	КонецЕсли;
	
	Если Не ВыводитьВопрос Тогда 
		ЗавершитьРаботуСистемы(Ложь);
		Возврат;
	КонецЕсли;
	
	УстановитьПроцедураЗавершитьРаботуПользователейУжеВыполняется(Истина);
	
	Оповещение = Новый ОписаниеОповещения("ЗавершитьРаботуЭтогоСеансаЗавершение", ЭтотОбъект);
	ТекстСообщения = НСтр("ru = 'Работа пользователей с программой запрещена. Завершить работу этого сеанса?'");
	Заголовок = НСтр("ru = 'Завершение работы текущего сеанса'");
	ПоказатьВопрос(Оповещение, ТекстСообщения, РежимДиалогаВопрос.ДаНет, 60, КодВозвратаДиалога.Да, Заголовок, КодВозвратаДиалога.Да);
	
КонецПроцедуры

Процедура ОбработкаОткрытияФормыБлокировкаРаботыПрограммы(Контекст) Экспорт
	
	ПолноеИмяФормы = "Обработка.БлокировкаРаботыПользователей.Форма.БлокировкаСоединенийСИнформационнойБазой";
	
	Окна = ПолучитьОкна();
	Для Каждого Окно Из Окна Цикл
		Для Каждого Форма Из Окно.Содержимое Цикл
			Если Форма.ИмяФормы = ПолноеИмяФормы И Форма.Открыта() Тогда
				Форма.Активизировать();
				Возврат;
			КонецЕсли;
		КонецЦикла;
	КонецЦикла;
	
	ОткрытьФорму(ПолноеИмяФормы);
	
КонецПроцедуры

///////////////////////////////////////////////////////////////////////////////
// Обработчики событий подсистемы БазоваяФункциональность.

Функция РаботаПользователейЗавершается() Экспорт
	
	Возврат ПараметрыКлиента().РаботаПользователейЗавершается;
	
КонецФункции

Функция ЗавершитьВсеСеансыКромеТекущего()
	
	Возврат ПараметрыКлиента().ЗавершитьВсеСеансыКромеТекущего;
	
КонецФункции

Функция СохраненныеПараметрыАдминистрирования()
	
	Возврат ПараметрыКлиента().ПараметрыАдминистрирования;
	
КонецФункции

Процедура СохранитьПараметрыАдминистрирования(Значение) Экспорт
	
	ПараметрыКлиента().ПараметрыАдминистрирования = Значение;
	
КонецПроцедуры

Процедура ЗаполнитьПараметрыАдминистрированияКластера(ПараметрыЗапуска)
	
	ПараметрыАдминистрирования = СоединенияИБВызовСервера.ПараметрыАдминистрирования();
	КоличествоПараметров = ПараметрыЗапуска.Количество();
	
	Если КоличествоПараметров > 1 Тогда
		ПараметрыАдминистрирования.ИмяАдминистратораКластера = ПараметрыЗапуска[1];
	КонецЕсли;
	
	Если КоличествоПараметров > 2 Тогда
		ПараметрыАдминистрирования.ПарольАдминистратораКластера = ПараметрыЗапуска[2];
	КонецЕсли;
	
	СохранитьПараметрыАдминистрирования(ПараметрыАдминистрирования);
	
КонецПроцедуры

///////////////////////////////////////////////////////////////////////////////
// Обработчики оповещений.

// Предлагает снять блокировку и войти или прекратить работу системы.
Процедура ИнтерактивнаяОбработкаПередНачаломРаботыСистемы(Параметры, Контекст) Экспорт
	
	ПараметрыКлиента = СтандартныеПодсистемыКлиент.ПараметрыРаботыКлиентаПриЗапуске();
	
	ТекстВопроса   = ПараметрыКлиента.ПредложениеВойти;
	ТекстСообщения = ПараметрыКлиента.СеансыОбластиДанныхЗаблокированы;
	
	Если Не ПустаяСтрока(ТекстВопроса) Тогда
		Кнопки = Новый СписокЗначений();
		Кнопки.Добавить(КодВозвратаДиалога.Да, НСтр("ru = 'Войти'"));
		Если ПараметрыКлиента.ВозможноСнятьБлокировку Тогда
			Кнопки.Добавить(КодВозвратаДиалога.Нет, НСтр("ru = 'Снять блокировку и войти'"));
		КонецЕсли;
		Кнопки.Добавить(КодВозвратаДиалога.Отмена, НСтр("ru = 'Отмена'"));
		
		ОбработкаОтвета = Новый ОписаниеОповещения(
			"ПослеОтветаНаВопросВойтиИлиСнятьБлокировку", ЭтотОбъект, Параметры);
		
		ПоказатьВопрос(ОбработкаОтвета, ТекстВопроса, Кнопки, 15,
			КодВозвратаДиалога.Отмена,, КодВозвратаДиалога.Отмена);
		Возврат;
	Иначе
		Параметры.Отказ = Истина;
		ПоказатьПредупреждение(
			СтандартныеПодсистемыКлиент.ОповещениеБезРезультата(Параметры.ОбработкаПродолжения),
			ТекстСообщения, 15);
	КонецЕсли;
	
КонецПроцедуры

// Продолжение предыдущей процедуры.
Процедура ПослеОтветаНаВопросВойтиИлиСнятьБлокировку(Ответ, Параметры) Экспорт
	
	Если Ответ = КодВозвратаДиалога.Да Тогда // Входим в заблокированное приложение.
		
	ИначеЕсли Ответ = КодВозвратаДиалога.Нет Тогда // Снимаем блокировку и входим в приложение.
		СоединенияИБВызовСервера.УстановитьБлокировкуСеансовОбластиДанных(
			Новый Структура("Установлена", Ложь));
	Иначе
		Параметры.Отказ = Истина;
	КонецЕсли;
	
	ВыполнитьОбработкуОповещения(Параметры.ОбработкаПродолжения);
	
КонецПроцедуры

Процедура ПоказатьПредупреждениеПриЗавершенииРаботы(ТекстСообщения, ВремяНачалаБлокировки)
	
	ПараметрыИнформирования = ПараметрыИнформирования(ВремяНачалаБлокировки);
	Если Не ПараметрыИнформирования.ОповещениеПоказано Тогда
		ПоказатьОповещениеПользователя(НСтр("ru = 'Работа программы будет завершена'"),, ТекстСообщения,, 
			СтатусОповещенияПользователя.Важное, "КонтрольЗавершенияРаботыПользователей");
		ПараметрыИнформирования.ОповещениеПоказано = Истина;
	КонецЕсли;
	
	Если Не ПараметрыИнформирования.ПоказатьПредупреждениеИлиВопрос Тогда
		Возврат;
	КонецЕсли;
	
	ПоказатьПредупреждение(, ТекстСообщения, 30);
	
КонецПроцедуры

// Возвращаемое значение:
//  Структура:
//   * ОповещениеПоказано - Булево
//   * ПоказатьПредупреждениеИлиВопрос - Булево
//
Функция ПараметрыИнформирования(ВремяНачалаБлокировки)
	
	ИмяПараметра = "СтандартныеПодсистемы.ПоказаноПредупреждениеПередЗавершениемРаботы";
	Свойства = ПараметрыПриложения[ИмяПараметра];
	Если Свойства = Неопределено Или Свойства.ВремяНачалаБлокировки <> ВремяНачалаБлокировки Тогда
		Свойства = Новый Структура;
		Свойства.Вставить("ОповещениеПоказано", Ложь);
		Свойства.Вставить("ПоказатьПредупреждениеИлиВопрос", Ложь);
		Свойства.Вставить("ДатаПоследнегоПредупрежденияИлиВопроса", '00010101');
		Свойства.Вставить("ВремяНачалаБлокировки", ВремяНачалаБлокировки);
		ПараметрыПриложения.Вставить(ИмяПараметра, Свойства);
	КонецЕсли;
	
	ДатаСеанса = ОбщегоНазначенияКлиент.ДатаСеанса();
	Если Свойства.ДатаПоследнегоПредупрежденияИлиВопроса + 50 < ДатаСеанса Тогда
		Свойства.ДатаПоследнегоПредупрежденияИлиВопроса = ДатаСеанса;
		Свойства.ПоказатьПредупреждениеИлиВопрос = Истина;
	Иначе
		Свойства.ПоказатьПредупреждениеИлиВопрос = Ложь;
	КонецЕсли;
	
	Возврат Свойства;
	
КонецФункции

Процедура ОчиститьОповещениеПользователя()
	
	ИмяПараметра = "СтандартныеПодсистемы.ПоказаноПредупреждениеПередЗавершениемРаботы";
	ПараметрыИнформирования = ПараметрыПриложения[ИмяПараметра];
	Если ПараметрыИнформирования = Неопределено
	 Или Не ПараметрыИнформирования.ОповещениеПоказано Тогда
		Возврат;
	КонецЕсли;
	ПараметрыИнформирования.ОповещениеПоказано = Ложь;
	
	ПоказатьОповещениеПользователя(НСтр("ru = 'Завершение работы отменено'"),, ,,
		СтатусОповещенияПользователя.Важное, "КонтрольЗавершенияРаботыПользователей");
	
КонецПроцедуры

Процедура ЗадатьВопросПриЗавершенииРаботы(ТекстСообщения, ВремяНачалаБлокировки)
	
	ПараметрыИнформирования = ПараметрыИнформирования(ВремяНачалаБлокировки);
	Если Не ПараметрыИнформирования.ОповещениеПоказано Тогда
		ПоказатьОповещениеПользователя(НСтр("ru = 'Работа программы будет завершена'"),, ТекстСообщения,,
			СтатусОповещенияПользователя.Важное, "КонтрольЗавершенияРаботыПользователей");
		ПараметрыИнформирования.ОповещениеПоказано = Истина;
	КонецЕсли;
	
	Если Не ПараметрыИнформирования.ПоказатьПредупреждениеИлиВопрос Тогда
		Возврат;
	КонецЕсли;
	
	ТекстВопроса = НСтр("ru = '%1
		|Завершить работу?'");
	ТекстВопроса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстВопроса, ТекстСообщения);
	ОписаниеОповещения = Новый ОписаниеОповещения("ЗадатьВопросПриЗавершенииРаботыЗавершение", ЭтотОбъект);
	ПоказатьВопрос(ОписаниеОповещения, ТекстВопроса, РежимДиалогаВопрос.ДаНет, 30, КодВозвратаДиалога.Да);
	
КонецПроцедуры

Процедура ЗадатьВопросПриЗавершенииРаботыЗавершение(Ответ, ДополнительныеПараметры) Экспорт
	
	Если Ответ = КодВозвратаДиалога.Да Тогда
		СтандартныеПодсистемыКлиент.ПропуститьПредупреждениеПередЗавершениемРаботыСистемы();
		ЗавершитьРаботуСистемы(Истина, Ложь);
	КонецЕсли;
	
КонецПроцедуры

Процедура ЗавершитьРаботуЭтогоСеансаЗавершение(Ответ, Параметры) Экспорт
	
	Если Ответ <> КодВозвратаДиалога.Нет Тогда
		СтандартныеПодсистемыКлиент.ПропуститьПредупреждениеПередЗавершениемРаботыСистемы();
		ЗавершитьРаботуСистемы(Ложь);
	КонецЕсли;
	
	УстановитьПроцедураЗавершитьРаботуПользователейУжеВыполняется(Ложь);
	
КонецПроцедуры	

// Обработать параметры запуска, связанные с завершением и разрешением соединений ИБ.
//
// Параметры:
//  ЗначениеПараметраЗапуска  - Строка - главный параметр запуска.
//  ПараметрыЗапуска          - Массив из Строка - дополнительные параметры запуска, разделенные символом ";".
//
// Возвращаемое значение:
//   Булево   - Истина, если требуется прекратить выполнение запуска системы.
//
Функция ОбработатьПараметрыЗапуска(Знач ПараметрыЗапуска)

	Если Не ОбщегоНазначенияКлиент.ДоступноИспользованиеРазделенныхДанных() Тогда
		Возврат Ложь;
	КонецЕсли;
	
	// Обработка параметров запуска программы ЗапретитьРаботуПользователей и РазрешитьРаботуПользователей.
	ИмяПараметраРазрешитьРаботуПользователей = "РазрешитьРаботуПользователей";
	ИмяПараметраЗавершитьРаботуПользователей = "ЗавершитьРаботуПользователей";
	Если КлючСодержитсяВПараметрахЗапуска(ПараметрыЗапуска, ИмяПараметраРазрешитьРаботуПользователей) Тогда
		
		Если НЕ СоединенияИБВызовСервера.РазрешитьРаботуПользователей() Тогда
			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Параметр запуска %1 не отработан. Нет прав на администрирование информационной базы.'"),
				ИмяПараметраРазрешитьРаботуПользователей);
			ПоказатьПредупреждение(,ТекстСообщения);
			Возврат Ложь;
		КонецЕсли;
		
		ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(СобытиеЖурналаРегистрации(),,
			СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Выполнен запуск с параметром %1. Работа программы будет завершена.'"),
					ИмяПараметраРазрешитьРаботуПользователей), ,Истина);
		ЗавершитьРаботуСистемы(Ложь);
		Возврат Истина;
		
	// Параметр может содержать две дополнительные части, разделенные символом ";" - 
	// имя и пароль администратора ИБ, от имени которого происходит подключение к кластеру серверов
	// в клиент-серверном варианте. См. использование в процедуре ЗавершитьРаботуПользователей.
	ИначеЕсли КлючСодержитсяВПараметрахЗапуска(ПараметрыЗапуска, ИмяПараметраЗавершитьРаботуПользователей) Тогда
		
		ДополнительныеПараметры = ДополнительныеПараметрыЗавершенияРаботыПользователей();
		
		БлокировкаУстановлена = СоединенияИБВызовСервера.УстановитьБлокировкуСоединений(
			ДополнительныеПараметры.ТекстСообщения,
			ДополнительныеПараметры.КодРазрешения,
			ДополнительныеПараметры.ОжиданиеНачалаБлокировкиМин,
			ДополнительныеПараметры.ДлительностьБлокировкиМин);
		
		Если НЕ БлокировкаУстановлена Тогда 
			ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Параметр запуска %1 не отработан. Нет прав на администрирование информационной базы.'"),
				ИмяПараметраЗавершитьРаботуПользователей);
			ПоказатьПредупреждение(,ТекстСообщения);
			Возврат Ложь;
		КонецЕсли;
		
		// Если выполнен запуск с ключом, то нужно зачитать параметры администрирования кластера.
		ПараметрыЗапускаУточненные = ПараметрыЗапускаУточненные(ПараметрыЗапуска, ДополнительныеПараметры);
		ЗаполнитьПараметрыАдминистрированияКластера(ПараметрыЗапускаУточненные);
		
		УстановитьПризнакРаботаПользователейЗавершается(Истина);
		ТекущийРежим = СоединенияИБВызовСервера.ПараметрыБлокировкиСеансов(Истина);
		ЗавершитьРаботуПользователей(ТекущийРежим);
		Возврат Ложь; 
		
	КонецЕсли;
	Возврат Ложь;
	
КонецФункции

// Возвращает строковую константу для формирования сообщений журнала регистрации.
//
// Возвращаемое значение:
//   Строка
//
Функция СобытиеЖурналаРегистрации() Экспорт
	
	Возврат НСтр("ru = 'Завершение работы пользователей'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка());
	
КонецФункции

#Область ДополнительныеПараметрыЗавершенияРаботыПользователей

// Извлекает параметры блокировки сеанса из параметра запуска.
//
// Возвращаемое значение:
//   Структура:
//     * ИмяАдминистратораКластера    - Строка - имя администратор кластера серверов 1С.
//     * ПарольАдминистратораКластера - Строка - пароль администратор кластера серверов 1С.
//     * ТекстСообщения               - Строка - текст, который будет частью сообщения об ошибке
//                                               при попытке установки соединения с заблокированной
//                                               информационной базой.
//     * КодРазрешения                - Строка - строка, которая должна быть добавлена к параметру
//                                               командной строки "/uc" или к параметру строки
//                                               соединения "uc", чтобы установить соединение с
//                                               информационной базой несмотря на блокировку.
//                                               Не применимо для блокировки сеансов области данных.
//     * ОжиданиеНачалаБлокировкиМин  - Число -  время отсрочки начала блокировки в минутах.
//     * ДлительностьБлокировкиМин    - Число -  время длительности блокировки в минутах.
//
Функция ДополнительныеПараметрыЗавершенияРаботыПользователей() 
	
	ДополнительныеПараметры = Новый Структура;
	ДополнительныеПараметры.Вставить("ИмяАдминистратораКластера", "");
	ДополнительныеПараметры.Вставить("ПарольАдминистратораКластера", "");
	ДополнительныеПараметры.Вставить("ТекстСообщения", "");
	ДополнительныеПараметры.Вставить("КодРазрешения", "КодРазрешения");
	ДополнительныеПараметры.Вставить("ОжиданиеНачалаБлокировкиМин", 0);
	ДополнительныеПараметры.Вставить("ДлительностьБлокировкиМин", 0);
	
	ДополнительныеПараметрыИзвлеченные = ДополнительныеПараметрыЗавершенияРаботыПользователейИзвлеченные();
	
	Для Каждого Параметр Из ДополнительныеПараметры Цикл 
		
		ЗначениеПараметра = Неопределено;
		
		Если ДополнительныеПараметрыИзвлеченные.Свойство(Параметр.Ключ, ЗначениеПараметра)
			И ЗначениеЗаполнено(ЗначениеПараметра) Тогда 
			
			ДополнительныеПараметры[Параметр.Ключ] = ЗначениеПараметра;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат ДополнительныеПараметры;
	
КонецФункции

Функция ДополнительныеПараметрыЗавершенияРаботыПользователейИзвлеченные()
	
	ДополнительныеПараметры = Новый Структура;
	
	ПараметрЗавершенияРаботыПользователей = ПараметрЗавершенияРаботыПользователей();
	
	Если Не ЗначениеЗаполнено(ПараметрЗавершенияРаботыПользователей) Тогда 
		Возврат ДополнительныеПараметры;
	КонецЕсли;
	
	НачальныйНомер = СтрНайти(ПараметрЗавершенияРаботыПользователей, "ЗавершитьРаботуПользователей")
		+ СтрДлина("ЗавершитьРаботуПользователей");
	
	ДополнительныеПараметрыСтрокой = СокрЛП(Сред(ПараметрЗавершенияРаботыПользователей, НачальныйНомер));
	СоставДополнительныхПараметров = СтрРазделить(ДополнительныеПараметрыСтрокой, ",");
	ПредыдущийПараметр = Неопределено;
	
	Для Каждого Параметр Из СоставДополнительныхПараметров Цикл 
		
		ОписаниеПараметра = СтрРазделить(Параметр, "=");
		
		Если ОписаниеПараметра.Количество() <> 2 Тогда 
			
			Если ПредыдущийПараметр <> Неопределено
				И ЗначениеЗаполнено(ОписаниеПараметра[0]) Тогда 
				
				ЗначениеПараметра = ДополнительныеПараметры[ПредыдущийПараметр] + "," + ОписаниеПараметра[0];
				ДополнительныеПараметры.Вставить(ПредыдущийПараметр, ЗначениеПараметра);
				
			КонецЕсли;
			
			Продолжить;
			
		КонецЕсли;
		
		ДополнительныеПараметры.Вставить(СокрЛП(ОписаниеПараметра[0]), СокрЛП(ОписаниеПараметра[1]));
		ПредыдущийПараметр = СокрЛП(ОписаниеПараметра[0]);
		
	КонецЦикла;
	
	Возврат ДополнительныеПараметрыЗавершенияРаботыПользователейНормализованные(ДополнительныеПараметры);
	
КонецФункции

Функция ПараметрЗавершенияРаботыПользователей()
	
	СоставПараметровЗапуска = СтрРазделить(ПараметрЗапуска, ";", Ложь);
	
	Для Каждого Параметр Из СоставПараметровЗапуска Цикл 
		
		Если СтрНачинаетсяС(СокрЛП(Параметр), "ЗавершитьРаботуПользователей") Тогда 
			Возврат Параметр;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат "";
	
КонецФункции

Функция ДополнительныеПараметрыЗавершенияРаботыПользователейНормализованные(ДополнительныеПараметры)
	
	ДополнительныеПараметрыНормализованные = Новый Структура;
	
	КлючиДополнительныхПараметров = КлючиДополнительныхПараметровЗавершенияРаботыПользователей();
	
	Для Каждого ДополнительныйПараметр Из ДополнительныеПараметры Цикл 
		
		Для Каждого КлючДополнительногоПараметра Из КлючиДополнительныхПараметров Цикл 
			
			Если СтрНачинаетсяС(ДополнительныйПараметр.Ключ, КлючДополнительногоПараметра.Ключ) Тогда 
				
				ДополнительныеПараметрыНормализованные.Вставить(
					КлючДополнительногоПараметра.Значение, ДополнительныйПараметр.Значение);
				
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЦикла;
	
	ОтформатироватьДополнительныеПараметрыЗавершенияРаботыПользователей(ДополнительныеПараметрыНормализованные);
	
	Возврат ДополнительныеПараметрыНормализованные;
	
КонецФункции

Функция КлючиДополнительныхПараметровЗавершенияРаботыПользователей()
	
	КлючиДополнительныхПараметров = Новый Соответствие;
	КлючиДополнительныхПараметров.Вставить("Имя", "ИмяАдминистратораКластера");
	КлючиДополнительныхПараметров.Вставить("Пароль", "ПарольАдминистратораКластера");
	КлючиДополнительныхПараметров.Вставить("Сообщение", "ТекстСообщения");
	КлючиДополнительныхПараметров.Вставить("Код", "КодРазрешения");
	КлючиДополнительныхПараметров.Вставить("Ожидание", "ОжиданиеНачалаБлокировкиМин");
	КлючиДополнительныхПараметров.Вставить("Длительность", "ДлительностьБлокировкиМин");
	
	Возврат КлючиДополнительныхПараметров;
	
КонецФункции

Процедура ОтформатироватьДополнительныеПараметрыЗавершенияРаботыПользователей(ДополнительныеПараметры)
	
	ПараметрыПодлежащиеФорматированию = Новый Массив;
	ПараметрыПодлежащиеФорматированию.Добавить("ОжиданиеНачалаБлокировкиМин");
	ПараметрыПодлежащиеФорматированию.Добавить("ДлительностьБлокировкиМин");
	
	ОписаниеЧисла = Новый ОписаниеТипов("Число");
	
	Для Каждого Параметр Из ПараметрыПодлежащиеФорматированию Цикл 
		
		ЗначениеПараметра = Неопределено;
		
		Если Не ДополнительныеПараметры.Свойство(Параметр, ЗначениеПараметра) Тогда 
			Продолжить;
		КонецЕсли;
		
		ДоступныеСимволы = Новый Массив;
		
		КоличествоСимволов = СтрДлина(ЗначениеПараметра);
		
		Для НомерСимвола = 1 По КоличествоСимволов Цикл 
			
			Символ = Сред(ЗначениеПараметра, НомерСимвола, 1);
			
			Если СтрНайти("0123456789", Символ) > 0 Тогда 
				ДоступныеСимволы.Добавить(Символ);
			КонецЕсли;
			
		КонецЦикла;
		
		ЗначениеПараметраНормализованное = СтрСоединить(ДоступныеСимволы);
		
		Если ЗначениеЗаполнено(ЗначениеПараметраНормализованное) Тогда 
			ДополнительныеПараметры[Параметр] = ОписаниеЧисла.ПривестиЗначение(ЗначениеПараметраНормализованное);
		Иначе
			ДополнительныеПараметры[Параметр] = 0;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

Функция КлючСодержитсяВПараметрахЗапуска(ПараметрыЗапуска, Ключ)
	
	Для Каждого Параметр Из ПараметрыЗапуска Цикл 
		
		Если СтрНачинаетсяС(СокрЛП(Параметр), Ключ) Тогда 
			Возврат Истина;
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат Ложь;
	
КонецФункции

Функция ПараметрыЗапускаУточненные(ПараметрыЗапуска, ДополнительныеПараметры)
	
	ИмяАдминистратораКластера = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
		ДополнительныеПараметры, "ИмяАдминистратораКластера");
	
	Если Не ЗначениеЗаполнено(ИмяАдминистратораКластера) Тогда 
		Возврат ПараметрыЗапуска;
	КонецЕсли;
	
	ПараметрыЗапускаУточненные = Новый Массив;
	ПараметрыЗапускаУточненные.Добавить(ПараметрыЗапуска[0]);
	ПараметрыЗапускаУточненные.Добавить(ИмяАдминистратораКластера);
	
	ПарольАдминистратораКластера = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(
		ДополнительныеПараметры, "ПарольАдминистратораКластера");
	
	Если ЗначениеЗаполнено(ПарольАдминистратораКластера) Тогда 
		ПараметрыЗапускаУточненные.Добавить(ПарольАдминистратораКластера);
	КонецЕсли;
	
	Возврат ПараметрыЗапускаУточненные;
	
КонецФункции

#КонецОбласти
